﻿// Copyright © 2012 X-treem Software Limited
// Written by Jason Payne
// Licence: https://wpfgooglesearch.codeplex.com/license

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Xml;
using System.Xml.Linq;
using Xtreem.GoogleSearch.Core;
using Xtreem.GoogleSearch.DataContracts;
using Xtreem.GoogleSearch.AutoSuggest;

namespace Xtreem.GoogleSearch
{
    internal class SearchViewModel : NotificationObject
    {
        private readonly string _resultsTemplate;
        private readonly string _resultItemTemplate;

        private string _resultsHtml;
        private CancellationTokenSource _cancellationTokenSource;

        public SearchViewModel()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();

            using (Stream stream = assembly.GetManifestResourceStream("Xtreem.GoogleSearch.Resources.ResultsTemplate.htm"))
            using (var reader = new StreamReader(stream))
            {
                _resultsTemplate = reader.ReadToEnd();
            }

            using (Stream stream = assembly.GetManifestResourceStream("Xtreem.GoogleSearch.Resources.ResultItemTemplate.htm"))
            using (var reader = new StreamReader(stream))
            {
                _resultItemTemplate = reader.ReadToEnd();
            }

            SuggestionProvider = new GoogleSuggestionProvider();
            Search = new RelayCommand<string>(x => GetSearchResults(x));
        }

        public ICommand Search { get; set; }

        public IAutoSuggestDataProvider SuggestionProvider { get; private set; }

        public string ResultsHtml
        {
            get { return _resultsHtml; }
            set { SetValue(ref _resultsHtml, value, () => ResultsHtml); }
        }

        public void GetSearchResults(string searchString)
        {
            TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();

            Action<string> searchComplete = delegate(string answer)
            {
                var json = new DataContractJsonSerializer(typeof(GoogleResponseWrapper));
                var results = json.ReadObject(new MemoryStream(Encoding.UTF8.GetBytes(answer))) as GoogleResponseWrapper;

                var resultItemsHtml = new StringBuilder();
                foreach (Result result in results.responseData.results)
                {
                    resultItemsHtml.Append(string.Format(_resultItemTemplate, result.url, result.title, result.visibleUrl, result.content));
                }

                ResultsHtml = _resultsTemplate.Replace("<!--RESULTS-->", resultItemsHtml.ToString());
            };

            // Prevent race condition should a call be made to this function before another asynchronous call has completed
            if (_cancellationTokenSource != null)
            {
                _cancellationTokenSource.Cancel();
            }

            _cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken = _cancellationTokenSource.Token;
            Task.Factory.StartNew<string>(SearchAsync, searchString, cancellationToken).ContinueWith(x => searchComplete(x.Result), new CancellationToken(), TaskContinuationOptions.NotOnCanceled, uiScheduler);
        }

        private string SearchAsync(object searchString)
        {
            // Create the web request
            var request = WebRequest.Create("http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=" + searchString) as HttpWebRequest;

            // Get response
            using (var response = request.GetResponse() as HttpWebResponse)
            {
                // Get the response stream
                return new StreamReader(response.GetResponseStream()).ReadToEnd();
            }
        }
    }
}
